// 通用工具函数

import type { AppError, Progress, MemoryUsage, PerformanceMetrics } from '../types';

/**
 * 格式化文件大小
 */
export function formatFileSize(bytes: number, decimals: number = 2): string {
  if (bytes === 0) return '0 Bytes';
  
  const k = 1024;
  const dm = decimals < 0 ? 0 : decimals;
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
  
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  
  return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}

/**
 * 格式化时间
 */
export function formatTime(seconds: number): string {
  const hours = Math.floor(seconds / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);
  const remainingSeconds = Math.floor(seconds % 60);
  
  if (hours > 0) {
    return `${hours}h ${minutes}m ${remainingSeconds}s`;
  } else if (minutes > 0) {
    return `${minutes}m ${remainingSeconds}s`;
  } else {
    return `${remainingSeconds}s`;
  }
}

/**
 * 格式化日期
 */
export function formatDate(date: Date | number | string, format: string = 'YYYY-MM-DD HH:mm:ss'): string {
  const d = new Date(date);
  
  const year = d.getFullYear();
  const month = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  const hours = String(d.getHours()).padStart(2, '0');
  const minutes = String(d.getMinutes()).padStart(2, '0');
  const seconds = String(d.getSeconds()).padStart(2, '0');
  
  return format
    .replace('YYYY', String(year))
    .replace('MM', month)
    .replace('DD', day)
    .replace('HH', hours)
    .replace('mm', minutes)
    .replace('ss', seconds);
}

/**
 * 生成唯一ID
 */
export function generateId(prefix: string = ''): string {
  const timestamp = Date.now().toString(36);
  const random = Math.random().toString(36).substr(2, 9);
  return prefix ? `${prefix}_${timestamp}_${random}` : `${timestamp}_${random}`;
}

/**
 * 深度克隆对象
 */
export function deepClone<T>(obj: T): T {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  
  if (obj instanceof Date) {
    return new Date(obj.getTime()) as T;
  }
  
  if (obj instanceof Array) {
    return obj.map(item => deepClone(item)) as T;
  }
  
  if (typeof obj === 'object') {
    const cloned = {} as T;
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        cloned[key] = deepClone(obj[key]);
      }
    }
    return cloned;
  }
  
  return obj;
}

/**
 * 深度合并对象
 */
export function deepMerge<T extends Record<string, any>>(target: T, source: Partial<T>): T {
  const result = { ...target };
  
  for (const key in source) {
    if (source.hasOwnProperty(key)) {
      const sourceValue = source[key];
      const targetValue = result[key];
      
      if (
        typeof sourceValue === 'object' &&
        sourceValue !== null &&
        !Array.isArray(sourceValue) &&
        typeof targetValue === 'object' &&
        targetValue !== null &&
        !Array.isArray(targetValue)
      ) {
        result[key] = deepMerge(targetValue, sourceValue);
      } else {
        result[key] = sourceValue as any;
      }
    }
  }
  
  return result;
}

/**
 * 防抖函数
 */
export function debounce<T extends (...args: any[]) => any>(
  func: T,
  wait: number
): (...args: Parameters<T>) => void {
  let timeout: ReturnType<typeof setTimeout> | null = null;
  
  return (...args: Parameters<T>) => {
    if (timeout) {
      clearTimeout(timeout);
    }
    
    timeout = setTimeout(() => {
      func(...args);
    }, wait);
  };
}

/**
 * 节流函数
 */
export function throttle<T extends (...args: any[]) => any>(
  func: T,
  limit: number
): (...args: Parameters<T>) => void {
  let inThrottle: boolean;
  
  return (...args: Parameters<T>) => {
    if (!inThrottle) {
      func(...args);
      inThrottle = true;
      setTimeout(() => (inThrottle = false), limit);
    }
  };
}

/**
 * 重试函数
 */
export async function retry<T>(
  fn: () => Promise<T>,
  maxRetries: number = 3,
  delay: number = 1000,
  backoff: number = 2
): Promise<T> {
  let lastError: Error;
  
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error as Error;
      
      if (attempt === maxRetries) {
        throw lastError;
      }
      
      await new Promise(resolve => setTimeout(resolve, delay * Math.pow(backoff, attempt - 1)));
    }
  }
  
  throw lastError!;
}

/**
 * 创建进度对象
 */
export function createProgress(current: number, total: number, message?: string): Progress {
  const percentage = total > 0 ? (current / total) * 100 : 0;
  
  return {
    current,
    total,
    percentage,
    message
  };
}

/**
 * 获取内存使用情况
 */
export function getMemoryUsage(): MemoryUsage {
  if (typeof performance !== 'undefined' && (performance as any).memory) {
    const memory = (performance as any).memory;
    const used = memory.usedJSHeapSize;
    const total = memory.totalJSHeapSize;
    const limit = memory.jsHeapSizeLimit;
    
    return {
      used,
      total,
      percentage: total > 0 ? (used / total) * 100 : 0,
      limit
    };
  }
  
  // 降级方案
  return {
    used: 0,
    total: 0,
    percentage: 0
  };
}

/**
 * 创建性能指标对象
 */
export function createPerformanceMetrics(): PerformanceMetrics {
  return {
    startTime: performance.now(),
    operations: 0,
    errors: 0
  };
}

/**
 * 结束性能指标
 */
export function endPerformanceMetrics(metrics: PerformanceMetrics): PerformanceMetrics {
  return {
    ...metrics,
    endTime: performance.now(),
    duration: performance.now() - metrics.startTime,
    memoryUsage: getMemoryUsage()
  };
}

/**
 * 创建应用错误
 */
export function createAppError(
  code: string,
  message: string,
  details?: any,
  originalError?: Error
): AppError {
  const error: AppError = {
    code,
    message,
    details,
    timestamp: Date.now()
  };
  
  if (originalError) {
    error.stack = originalError.stack;
  }
  
  return error;
}

/**
 * 检查是否为网络错误
 */
export function isNetworkError(error: any): boolean {
  return (
    error instanceof TypeError &&
    (error.message.includes('Network Error') ||
     error.message.includes('Failed to fetch') ||
     error.message.includes('Network request failed'))
  );
}

/**
 * 检查是否为超时错误
 */
export function isTimeoutError(error: any): boolean {
  return (
    error.name === 'TimeoutError' ||
    error.code === 'ECONNABORTED' ||
    error.message.includes('timeout')
  );
}

/**
 * 检查是否为取消错误
 */
export function isCancelError(error: any): boolean {
  return (
    error.name === 'CancelError' ||
    error.code === 'ERR_CANCELED' ||
    error.message.includes('canceled')
  );
}

/**
 * 验证URL
 */
export function isValidUrl(url: string): boolean {
  try {
    new URL(url);
    return true;
  } catch {
    return false;
  }
}

/**
 * 验证文件扩展名
 */
export function isValidFileExtension(fileName: string, allowedExtensions: string[]): boolean {
  if (!allowedExtensions || allowedExtensions.length === 0) {
    return true;
  }
  
  const extension = fileName.toLowerCase().split('.').pop();
  return allowedExtensions.map(ext => ext.toLowerCase()).includes(extension || '');
}

/**
 * 验证文件大小
 */
export function isValidFileSize(fileSize: number, maxSize: number): boolean {
  return maxSize <= 0 || fileSize <= maxSize;
}

/**
 * 获取文件扩展名
 */
export function getFileExtension(fileName: string): string {
  return fileName.toLowerCase().split('.').pop() || '';
}

/**
 * 获取文件MIME类型
 */
export function getFileMimeType(fileName: string): string {
  const extension = getFileExtension(fileName);
  const mimeTypes: Record<string, string> = {
    'dcm': 'application/dicom',
    'dic': 'application/dicom',
    'dicom': 'application/dicom',
    'zip': 'application/zip',
    'jpg': 'image/jpeg',
    'jpeg': 'image/jpeg',
    'png': 'image/png',
    'gif': 'image/gif',
    'bmp': 'image/bmp',
    'tiff': 'image/tiff',
    'webp': 'image/webp'
  };
  
  return mimeTypes[extension] || 'application/octet-stream';
}

/**
 * 数组分块
 */
export function chunkArray<T>(array: T[], size: number): T[][] {
  const chunks: T[][] = [];
  for (let i = 0; i < array.length; i += size) {
    chunks.push(array.slice(i, i + size));
  }
  return chunks;
}

/**
 * 数组去重
 */
export function uniqueArray<T>(array: T[], key?: keyof T): T[] {
  if (!key) {
    return [...new Set(array)];
  }
  
  const seen = new Set();
  return array.filter(item => {
    const value = item[key];
    if (seen.has(value)) {
      return false;
    }
    seen.add(value);
    return true;
  });
}

/**
 * 对象数组排序
 */
export function sortArrayBy<T>(
  array: T[],
  key: keyof T,
  order: 'asc' | 'desc' = 'asc'
): T[] {
  return [...array].sort((a, b) => {
    const valueA = a[key];
    const valueB = b[key];
    
    if (valueA < valueB) {
      return order === 'asc' ? -1 : 1;
    }
    if (valueA > valueB) {
      return order === 'asc' ? 1 : -1;
    }
    return 0;
  });
}

/**
 * 等待函数
 */
export function sleep(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

/**
 * 轮询函数
 */
export async function poll<T>(
  fn: () => Promise<T>,
  condition: (result: T) => boolean,
  interval: number = 1000,
  timeout: number = 30000
): Promise<T> {
  const startTime = Date.now();
  
  while (Date.now() - startTime < timeout) {
    const result = await fn();
    
    if (condition(result)) {
      return result;
    }
    
    await sleep(interval);
  }
  
  throw new Error('Polling timeout');
}

/**
 * 批量处理函数
 */
export async function batchProcess<T, R>(
  items: T[],
  processor: (item: T) => Promise<R>,
  batchSize: number = 10,
  delay: number = 0
): Promise<R[]> {
  const results: R[] = [];
  const batches = chunkArray(items, batchSize);
  
  for (const batch of batches) {
    const batchResults = await Promise.all(batch.map(processor));
    results.push(...batchResults);
    
    if (delay > 0 && batches.indexOf(batch) < batches.length - 1) {
      await sleep(delay);
    }
  }
  
  return results;
}

/**
 * 缓存装饰器
 */
export function cache<T extends (...args: any[]) => any>(
  ttl: number = 60000
): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void {
  const originalMethod = descriptor.value;
  const cache = new Map<string, { value: any; timestamp: number }>();
  
  descriptor.value = function (...args: Parameters<T>): ReturnType<T> {
    const key = JSON.stringify(args);
    const now = Date.now();
    
    const cached = cache.get(key);
    if (cached && now - cached.timestamp < ttl) {
      return cached.value;
    }
    
    const result = originalMethod.apply(this, args);
    cache.set(key, { value: result, timestamp: now });
    
    return result;
  };
}

/**
 * 性能监控装饰器
 */
export function monitorPerformance(
  target: any,
  propertyKey: string,
  descriptor: PropertyDescriptor
): void {
  const originalMethod = descriptor.value;
  
  descriptor.value = function (...args: any[]) {
    const startTime = performance.now();
    
    try {
      const result = originalMethod.apply(this, args);
      const endTime = performance.now();
      
      console.log(`${propertyKey} executed in ${endTime - startTime}ms`);
      
      return result;
    } catch (error) {
      const endTime = performance.now();
      console.error(`${propertyKey} failed after ${endTime - startTime}ms:`, error);
      throw error;
    }
  };
}
